package com.yaolonga.itfc.manager.linux;

import cn.hutool.core.map.MapUtil;
import cn.hutool.cron.Scheduler;
import com.yaolonga.itfc.manager.core.*;
import com.yaolonga.itfc.manager.core.exception.CmdExecuteException;
import com.yaolonga.itfc.manager.core.utils.CommandUtils;
import com.yaolonga.itfc.manager.core.utils.NetUtil;
import com.yaolonga.itfc.manager.core.utils.SystemUtil;
import com.yaolonga.itfc.manager.core.wifi.*;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: yaolong
 * @CreateTime: 2024-11-26  15:51
 * @Description: (linux nmcli)网卡信息提供者
 * @Version: 1.0
 */
@Slf4j
public class NmcliNetworkInterfaceManager extends AbstractNetworkInterfaceManager {

    private static final String ETH_TYPE = "ethernet";
    private static final String WIFI_TYPE = "wifi";
    private static final String PROFILE_PREFIX = "ylItfcMng";
    private static final String DEFAULT_ROUTE_METRIC = "201";

    private final List<String> connectionConfYesValue = Arrays.asList("yes", "y", "Y", "YES", "是");
    private final List<String> connectionConfNoValue = Arrays.asList("no", "n", "N", "NO", "否");
    private final List<String> connectionConfAutoValue = Arrays.asList("auto", "AUTO", "自动");
    private final List<String> connectionConfActivatedValue = Arrays.asList("activated", "ACTIVATED", "已激活");

    @Override
    public PlatformType getPlatformType() {
        return PlatformType.linux;
    }

    @Override
    public SystemType getSystemType() {
        return SystemType.ubuntu;
    }


    public NmcliNetworkInterfaceManager() {
        try {
            // 异步线程初始化网卡连接配置
            // linux系统才启动
            if (SystemUtil.isLinux()){
                initConnectionProfile();
            }
        } catch (Exception ignored) {
        }
    }

    /**
     * 初始化网卡连接配置（会创建一个名为yl）
     */
    private void initConnectionProfile() {
        getNetworkInterfaceInfos()
                .stream().filter(networkInterfaceInfo -> !networkInterfaceInfo.getConfName().equals("--"))
                .forEach(networkInterfaceInfo -> {
                    if (networkInterfaceInfo.getType() == InterfaceType.wifi) {
                        checkEthProfileAndCreate(networkInterfaceInfo.getConfName(), networkInterfaceInfo.getId(), networkInterfaceInfo.getType().linuxType);
                    } else {
                        checkEthProfileAndCreate(getProfileName(networkInterfaceInfo.getId()), networkInterfaceInfo.getId(), networkInterfaceInfo.getType().linuxType);
                    }
                });
    }

    /**
     * 获取网络配置输入流
     */
    protected InputStream getNetConfInputStream() {
        return getNetConfInputStream(null);
    }

    private String getProfileName(String profileName) {
        if (profileName.startsWith(PROFILE_PREFIX)) {
            return profileName;
        }
        return PROFILE_PREFIX + "-" + profileName;
    }

    private String getWifiProfileName(String profileName, String interfaceName) {
        if (profileName.startsWith(PROFILE_PREFIX + "-" + interfaceName)) {
            return profileName;
        }
        return PROFILE_PREFIX + "-" + interfaceName + "-" + profileName;
    }


    protected InputStream getNetConfInputStream(String interfaceName) {
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("dev");
        cmds.add("show");
        if (interfaceName == null) {
            return execCmd(cmds).getInputStream();
        }
        cmds.add(interfaceName);
        return execCmd(cmds).getInputStream();
    }

    @Override
    public List<NetworkInterfaceInfo> getNetworkInterfaceInfos() {
        InputStream netConfInputStream = getNetConfInputStream();
        return doGetNetworkInterfaceInfos(netConfInputStream);
    }

    private List<NetworkInterfaceInfo> doGetNetworkInterfaceInfos(InputStream netConfInputStream) {
        Map<String, Map<String, Object>> netMap = getNetMap();
        List<NetworkInterfaceInfo> infos = new ArrayList<>();
        if (netConfInputStream != null) {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(netConfInputStream, getCharset()));
            String line;
            try {
                // 是否忽略行
                boolean ignoreLine = false;
                NetworkInterfaceInfo info = null;
                while ((line = bufferedReader.readLine()) != null) {
                    if (line.trim().isEmpty()) {
                        continue;
                    }
                    if (line.contains("GENERAL.DEVICE:")) {
                        info = new NetworkInterfaceInfo();
                        infos.add(info);
                        String[] split = line.split(": ");
                        if (split.length > 1) {
                            String value = split[1].trim();
                            Map<String, Object> metaInfo = netMap.get(value);
                            info.setId(value);
                            info.setDescription(value);
                            info.setName(value);
                            if (metaInfo != null) {
                                Object ifIndex = metaInfo.get("ifIndex");
                                info.setIfIndex((Integer) ifIndex);
                                Object dhcp = metaInfo.get("dhcp");
                                info.setDhcp((Boolean) dhcp);
                            }

                        }
                        continue;
                    }

                    if (line.contains("GENERAL.CONNECTION:")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            info.setConfName(value);
                        }
                        continue;
                    }

                    if (line.contains("GENERAL.TYPE:")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            info.setType(InterfaceType.ofLinuxType(value));
                        }
                        continue;
                    }

                    if (line.contains("GENERAL.STATE:")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String trim = split[1].trim();
                            boolean value = trim.startsWith("100");
                            info.setState(value);
                        }
                        continue;
                    }

                    if (line.contains("GENERAL.MTU:")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            info.setMtu(Integer.parseInt(value));
                        }
                        continue;
                    }

                    if (line.contains("GENERAL.HWADDR:")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            info.setMac(value);
                        }
                        continue;
                    }


                    if (line.startsWith("IP4.ADDRESS")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String[] split1 = split[1].trim().split("/");
                            String value = split1[0];
                            if (split[0].contains("[1]")) {
                                info.addIp(NetworkAddress.of(value, true));
                            } else {
                                info.addIp(NetworkAddress.of(value, false));
                            }
                            int mask = Integer.parseInt(split1[1]);
                            info.setMask(NetUtil.getMaskIp(mask));
                        }
                        continue;
                    }
                    if (line.contains("IP4.GATEWAY:")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            if (!value.equals("--")) {
                                info.setGateway(value);
                            }

                        }
                        continue;
                    }

                    if (line.contains("IP4.DNS")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            if (split[0].contains("[1]")) {
                                info.addDns(NetworkAddress.of(value, true));
                            } else {
                                info.addDns(NetworkAddress.of(value, false));
                            }
                        }
                        continue;
                    }

                    if (line.contains("IP4.ROUTE")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String value = split[1].trim();
                            String[] split1 = value.split(",");
                            String s = split1[0].trim();
                            String[] split2 = s.split("=")[1].trim().split("/");
                            String target = split2[0].trim();
                            int mask = Integer.parseInt(split2[1].trim());
                            NetRoute netRoute = new NetRoute();
                            netRoute.setIfIndex(info.getIfIndex());


                            // 网络地址
                            netRoute.setTarget(target);
                            netRoute.setInterfaceName(info.getId());
                            netRoute.setMask(NetUtil.getMaskIp(mask));
                            if (!info.getIp().isEmpty()) {
                                netRoute.setIp(info.getIp().get(0).getIp());
                            }
                            // 网关
                            String s1 = split1[1].trim();
                            String[] split3 = s1.split("=");
                            String gateway = split3[1].trim();
                            if (!gateway.equals("--") && !gateway.equals("0.0.0.0")) {
                                // 如果网关为空则设置网关
                                if (null == info.getGateway()) {
                                    info.setGateway(gateway);
                                }
                            }
                            netRoute.setGateway(gateway);

                            // 跳数
                            String s2 = split1[2].trim();
                            String[] split4 = s2.split("=");
                            String metric = split4[1].trim();
                            netRoute.setMetric(metric);

                            // 设置路由类别
                            setRouteType(netRoute, info.getConfName());
                            info.addRoute(netRoute);
                        }
                        continue;
                    }

                    if (line.contains("IP6.ADDRESS")) {
                        String[] split = line.split(": ");
                        if (split.length > 1 && info != null) {
                            String[] split1 = split[1].trim().split("/");
                            String value = split1[0];
                            int mask = Integer.parseInt(split1[1]);

                            if (split[0].contains("[1]")) {
                                info.addIpV6(NetworkAddress.of(value, true));
                            } else {
                                info.addIpV6(NetworkAddress.of(value, false));
                            }
                        }
                    }
                }
            } catch (IOException ignored) {
            }
        }
        return infos.stream().filter(i -> !i.getType().equals(InterfaceType.unknown)).collect(Collectors.toList());
    }


    private List<NetRoute> getStaticRoutes(String interfaceName, String profileName) {
        List<NetRoute> routes = new ArrayList<>();
        try {
            String s = CommandUtils.execClassPathShFileAndReturn("nmcli-ext.sh", "-f getRoutes -n " + profileName, this.getClass().getClassLoader()).trim();
            if (!s.isEmpty()) {
                String[] split = s.split(": ");
                if (split.length > 1) {
                    String routesStr = split[1];
                    String[] _routes = routesStr.split(";");
                    for (String route : _routes) {
                        String _route = route.trim();
                        // 无路由直接退出
                        if (route.equals("--")) {
                            break;
                        }
                        String res = _route.substring(1, _route.length() - 2);
                        String[] r_item = res.split(",");
                        if (r_item.length >= 2) {
                            NetRoute netRoute = new NetRoute();
                            netRoute.setInterfaceName(interfaceName);
                            String ipAndMask = r_item[0].split("=")[1].trim();
                            String[] split1 = ipAndMask.split("/");
                            if (split1.length == 1) {
                                String ip = split1[0].trim();
                                netRoute.setTarget(ip);
                                netRoute.setMask("0.0.0.0");
                            }
                            if (split1.length == 2) {
                                String ip = split1[0].trim();
                                String mask = split1[1].trim();
                                netRoute.setTarget(ip);
                                netRoute.setMask(NetUtil.getMaskIp(Integer.parseInt(mask)));
                            }

                            String nh = r_item[1].split("=")[1].trim();
                            netRoute.setGateway(nh);
                            if (r_item.length == 3) {
                                String mt = r_item[2].split("=")[1].trim();
                                netRoute.setMetric(mt);
                            } else {
                                netRoute.setMetric(DEFAULT_ROUTE_METRIC);
                            }
                            netRoute.setType(NetRouteType.PERMANENT_ROUTE);
                            routes.add(netRoute);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.warn("static route get error:  {}", e.getMessage());
        }
        return routes;
    }

    private void setRouteType(NetRoute netRoute, String profileName) {
        List<NetRoute> routes = getStaticRoutes(netRoute.getInterfaceName(), profileName);
        // 如果路由存在则为永久路由
        if (routes.stream().anyMatch(route ->
                route.getTarget().equals(netRoute.getTarget()) &&
                        route.getMask().equals(netRoute.getMask()) &&
                        route.getGateway().equals(netRoute.getGateway()) &&
                        route.getMetric().equals(netRoute.getMetric())
        )) {
            netRoute.setType(NetRouteType.PERMANENT_ROUTE);
        } else {
            // 如果路由不存在则为临时路由
            netRoute.setType(NetRouteType.TEMPORARY_ROUTE);
        }
    }

    @Override
    public NetworkInterfaceInfo getNetworkInterfaceInfo(String interfaceName) {
        InputStream netConfInputStream = getNetConfInputStream(interfaceName);
        return doGetNetworkInterfaceInfos(netConfInputStream).stream().findFirst().orElse(null);
    }


    private Map<String, Map<String, Object>> getNetMap() {
        HashMap<String, Map<String, Object>> res = new HashMap<>();
        InputStream inputStream = execCmd("ip -o addr show").getInputStream();
        if (inputStream != null) {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, getCharset()));
            String line;
            try {
                // 是否忽略行
                boolean ignoreLine = false;
                while ((line = bufferedReader.readLine()) != null) {
                    // 忽略ipv6
                    if (line.contains("inet6")) {
                        continue;
                    }
                    String[] split = line.split(": ");
                    if (split.length > 1) {
                        int ifIndex = Integer.parseInt(split[0].trim());
                        String[] split1 = split[1].trim().split(" ");
                        String itfc = split1[0].trim();
                        boolean dhcp = false;
                        if (line.contains("global dynamic")) {
                            dhcp = true;
                        }
                        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
                        stringObjectHashMap.put("dhcp", dhcp);
                        stringObjectHashMap.put("ifIndex", ifIndex);
                        res.put(itfc, stringObjectHashMap);
                    }
                }
            } catch (IOException ignore) {
            }
        }

        return res;
    }

    private String applyConf(String profileName) {
        return execCmdAndReturn("nmcli con up " + profileName);
    }


    @Override
    public String configDhcpIpAddress(String interfaceName) {
        String profileName = getCompatibilityProfileName(interfaceName);
        if (interfaceName == null) {
            throw new IllegalArgumentException("interfaceName 不能为空");
        }
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("mod");
        cmds.add(profileName);
        cmds.add("ipv4.method");
        cmds.add("auto");
        String s = applyConf(profileName);
        log.debug("配置ip为自动获取");
        return s;
    }

    @Override
    public String configDhcpDnsAddress(String interfaceName) {
        return "";
    }

    public static void main(String[] args) {
        NmcliNetworkInterfaceManager nmcliNetworkInterfaceManager = new NmcliNetworkInterfaceManager();
//        nmcliNetworkInterfaceManager.getStaticRoutes("ens33");
//        nmcliNetworkInterfaceManager.checkEthProfileAndCreate("eth0", "eth0", ETH_TYPE);
//        nmcliNetworkInterfaceManager.configStaticIpAddress("ens33", "10.1.31.77", "255.255.0.0", "10.1.1.1");
//        NetworkInterfaceInfo networkInterfaceInfo = nmcliNetworkInterfaceManager.getNetworkInterfaceInfo("ens33");
//        System.out.println(networkInterfaceInfo);
//        nmcliNetworkInterfaceManager.configStaticDnsAddress("ens33", "8.8.8.8");

//        nmcliNetworkInterfaceManager.addDnsAddress("ens33","116.116.116.116");
//        nmcliNetworkInterfaceManager.addDnsAddress("ens33", "8.8.8.8", 0);
//        nmcliNetworkInterfaceManager.delDnsAddress("ens33","8.8.8.8");

//        NetRoute netRoute = new NetRoute();
//        netRoute.setType(NetRouteType.PERMANENT_ROUTE);
//        netRoute.setTarget("10.1.0.0");
//        netRoute.setMask("255.0.0.0");
//        netRoute.setGateway("10.1.1.1");
//        netRoute.setInterfaceName("ens33");
//        netRoute.setMetric("130");
//        nmcliNetworkInterfaceManager.addRoute(netRoute);
//        nmcliNetworkInterfaceManager.changeRoute(netRoute);
//        nmcliNetworkInterfaceManager.delRoute(netRoute);
//        WifiScanInfo wifiScanInfo = nmcliNetworkInterfaceManager.scanWifiInfo("wlx0025224ace64");
//        System.out.println(wifiScanInfo);

        WifiLinkConf conf = new WifiLinkConf();
        conf.setName("FH05");
        conf.setInterfaceName("wlx0025224ace64");
        conf.setSsid("FH05");
        conf.setSecureType(WifiSecureType.WPA2.name());
        conf.setAutoConnection(true);
        conf.setPassword("Wbrfid900AEI");
        String s = nmcliNetworkInterfaceManager.connectWifi(conf);

//        nmcliNetworkInterfaceManager.disconnectWifi("wlx0025224ace64");
//        System.out.println(s);
    }

    private String createDefaultConf(String profileName, String interfaceName, String netType) {
        String s = execCmdAndReturn(String.format("nmcli con add con-name %s type %s connection.autoconnect yes ipv4.method auto ipv4.route-metric %s ifname %s", profileName, netType, DEFAULT_ROUTE_METRIC, interfaceName));
        log.info("create conf name {} \n cmd return: {}", profileName, s);
        return s;
    }

    private void checkEthProfileAndCreate(String profileName, String interfaceName, String netType) {
        Process process = execCmd("nmcli con show");
        InputStream inputStream = process.getInputStream();
        Map<String, Map<String, Object>> profileSimpleInfo = new HashMap<>();
        if (null != inputStream) {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line;
            boolean isHeader = true;
            try {
                while ((line = bufferedReader.readLine()) != null) {
                    line = line.trim();
                    if (line.isEmpty()) {
                        continue;
                    }
                    if (isHeader) {
                        isHeader = false;
                        continue;
                    }

                    String[] split = line.split("\\s+");
                    if (split.length == 4) {
                        String name = split[0].trim();
                        String uuid = split[1].trim();
                        String type = split[2].trim();
                        String device = split[3].trim();
                        if (type.equals(netType)) {
                            profileSimpleInfo.put(name, MapUtil.<String, Object>builder()
                                    .put("name", name)
                                    .put("type", type)
                                    .put("uuid", uuid)
                                    .put("device", device)
                                    .build());
                        }
                    }
                }

                Map<String, Object> con = profileSimpleInfo.get(profileName);
                if (Objects.isNull(con)) {
                    // 当前接口是否已有配置文件，如果有直接复制并创建，否则就直接创建
                    Map<String, Object> conf = profileSimpleInfo.values().stream().filter(map -> interfaceName.equals(map.get("device"))).findFirst().orElse(null);

                    if (Objects.nonNull(conf)) {
                        // 配置文件名称
                        String fileName = (String) conf.get("name");
                        // 克隆一个配置文件
                        cloneConfFile(fileName, profileName);

                        // 修改路由metric 为默认值
                        execCmdAndReturn(String.format("nmcli con modify %s ipv4.route-metric %s", profileName, DEFAULT_ROUTE_METRIC));
                        log.info("modify  {}  ipv4.route-metric => {}", profileName, DEFAULT_ROUTE_METRIC);
                    } else {
                        // 创建一个默认配置文件1`
                        createDefaultConf(profileName, interfaceName, netType);
                    }
                }
                // 应用配置文件
                applyConf(profileName);
            } catch (Exception ignore) {
                log.error(ignore.getMessage());
            }

        }
    }

    /**
     * 克隆配置文件
     *
     * @param fileName
     * @param newProfileName
     */
    private String cloneConfFile(String fileName, String newProfileName) {
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("clone");
        cmds.add(fileName);
        cmds.add(newProfileName);
        String s = execCmdAndReturn(cmds);
        log.info("clone conf name {} \n cmd return: {}", newProfileName, s);
        return s;
    }

    @Override
    public String configStaticIpAddress(@NonNull String interfaceName, @NonNull String ip, @NonNull String mask, @NonNull String gateway) {
        String profileName = getCompatibilityProfileName(interfaceName);
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("mod");
        cmds.add(profileName);
        cmds.add("ipv4.method");
        cmds.add("manual");

        cmds.add("ipv4.addresses");
        cmds.add(ip + "/" + NetUtil.getMaskNum(mask));
        cmds.add("ipv4.gateway");
        cmds.add(gateway);
        cmds.add("ifname");
        cmds.add(interfaceName);
        String s = execCmdAndReturn(cmds);
        log.debug("conf static ip done: {}", s);
        String s1 = applyConf(profileName);
        log.debug("apply down: {}", s);
        return s1;
    }

    /**
     * 兼容性获取配置文件名称
     *
     * @param interfaceName 网卡名称
     * @return profileName
     */
    private String getCompatibilityProfileName(String interfaceName) {
        NetworkInterfaceInfo networkInterfaceInfo = getNetworkInterfaceInfo(interfaceName);
        if (null == networkInterfaceInfo) {
            throw new CmdExecuteException("nmcli dev show " + interfaceName, "interface " + interfaceName + " 不存在");
        }
        if (networkInterfaceInfo.getType() == InterfaceType.wifi) {
            return networkInterfaceInfo.getConfName();
        } else {
            return getProfileName(interfaceName);
        }
    }


    @Override
    public String configStaticDnsAddress(String interfaceName, String dnsAddress) {
        String profileName = getCompatibilityProfileName(interfaceName);

        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("mod");
        cmds.add(profileName);
        cmds.add("ipv4.dns");
        cmds.add(dnsAddress);
        execCmdAndReturn(cmds);
        String s = applyConf(profileName);
        log.debug("conf dns done {}", s);
        return s;
    }

    public List<String> getDns(String profileName) {
        String s = CommandUtils.execClassPathShFileAndReturn("nmcli-ext.sh", "-f getDns -n " + profileName, this.getClass().getClassLoader()).trim();
        String[] split = s.split("\\s+");
        if (split.length > 1) {
            String[] split1 = split[1].split(",");
            return Arrays.stream(split1).map(String::trim).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public String addDnsAddress(String interfaceName, String dnsAddress, int index) {
        String profileName = getCompatibilityProfileName(interfaceName);
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("mod");
        cmds.add(profileName);
        if (index != -1) {
            List<String> dns = getDns(profileName);
            cmds.add("ipv4.dns");
            dns.add(index, dnsAddress);
            cmds.add(String.join(",", dns));
        } else {
            cmds.add("+ipv4.dns");
            cmds.add(dnsAddress);
        }
        String s = execCmdAndReturn(cmds);
        String s1 = applyConf(profileName);
        log.debug("add dns done {}", s);
        return s1;
    }


    @Override
    public String addDnsAddress(String interfaceName, String dnsAddress) {
        return addDnsAddress(interfaceName, dnsAddress, -1);
    }


    @Override
    public String delDnsAddress(String interfaceName, String dnsAddress) {
        String profileName = getCompatibilityProfileName(interfaceName);
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("mod");
        cmds.add(profileName);
        cmds.add("-ipv4.dns");
        cmds.add(dnsAddress);
        String s = execCmdAndReturn(cmds);
        String s1 = applyConf(profileName);
        log.debug("del dns done {}", s);
        // 先删除所有的
        return s1;
    }

    @Override
    public String addRoute(NetRoute route) {
        route.validate();
        String profileName = getCompatibilityProfileName(route.getInterfaceName());
        List<String> cmds;
        NetworkInterfaceInfo networkInterfaceInfo = getNetworkInterfaceInfo(route.getInterfaceName());

        if (networkInterfaceInfo != null) {
            List<NetRoute> routes = networkInterfaceInfo.getRoutes();
            if (routes.stream().noneMatch(i ->
                    i.getTarget().equals(route.getTarget()) &&
                            i.getMask().equals(route.getMask()) &&
                            i.getGateway().equals(route.getGateway()) &&
                            i.getType().equals(route.getType()))
            ) {
                // 添加永久路由
                if (route.getType().equals(NetRouteType.PERMANENT_ROUTE)) {
                    cmds = new ArrayList<>();
                    cmds.add("nmcli");
                    cmds.add("con");
                    cmds.add("mod");
                    cmds.add(profileName);
                    cmds.add("+ipv4.routes");
                    cmds.add(String.format("%s %s %s", route.getTargetAndMask(), route.getGateway(), route.getMetric()));

                    String s = execCmdAndReturn(cmds);
                    String s1 = applyConf(profileName);
                    log.debug("add route done {}", s);
                    return s1;
                } else {
//                    ip route add 192.168.0.0/24 via 192.168.137.1 metric 200
                    cmds = new ArrayList<>();
                    cmds.add("ip");
                    cmds.add("route");
                    cmds.add("add");
                    cmds.add(route.getTargetAndMask());
                    cmds.add("via");
                    cmds.add(route.getGateway());
                    if (route.getMetric() != null) {
                        cmds.add("metric");
                        cmds.add(route.getMetric());
                    }
                    // 临时路由
                    String s = execCmdAndReturn(cmds);
                    return s;

                }
            }

        }
        return "";
    }

    /**
     * CHANGE  只用于修改网关和/或跃点数。
     *
     * @param route 路由配置
     * @return 结果
     */
    @Override
    public String changeRoute(NetRoute route) {
        route.validate();
        String profileName = getCompatibilityProfileName(route.getInterfaceName());
        NetworkInterfaceInfo networkInterfaceInfo = getNetworkInterfaceInfo(route.getInterfaceName());

        // 如果是临时路由直接修改，否则永久路由则修改netplan文件
        if (route.getType().equals(NetRouteType.TEMPORARY_ROUTE)) {
            List<String> cmds = new ArrayList<>();
            cmds.add("ip");
            cmds.add("route");
            cmds.add("change");
            cmds.add(route.getTargetAndMask());
            if (null != route.getGateway()) {
                cmds.add("via");
                cmds.add(route.getGateway());
            }
            if (null != route.getMetric()) {
                cmds.add("metric");
                cmds.add(route.getMetric());
            }
            cmds.add("dev");
            cmds.add(route.getInterfaceName());
            return execCmdAndReturn(cmds);
        } else if (route.getType().equals(NetRouteType.PERMANENT_ROUTE)) {
            List<NetRoute> routes = networkInterfaceInfo.getRoutes();
            String routs = routes.stream().filter(i -> i.getType().equals(NetRouteType.PERMANENT_ROUTE))
                    .map(i -> {
                        if (i.getTargetAndMask().equals(route.getTargetAndMask())) {
                            i.setMetric(route.getMetric());
                            i.setGateway(route.getGateway());
                        }
                        if (i.getMetric() == null) {
                            return String.format("%s %s", i.getTargetAndMask(), i.getGateway());
                        } else {
                            return String.format("%s %s %s", i.getTargetAndMask(), i.getGateway(), i.getMetric());
                        }
                    }).collect(Collectors.joining(","));
            List<String> cmds = new ArrayList<>();
            cmds.add("nmcli");
            cmds.add("con");
            cmds.add("mod");
            cmds.add(profileName);
            cmds.add("ipv4.routes");
            cmds.add(routs);
            String s = execCmdAndReturn(cmds);
            String s1 = applyConf(profileName);
            log.debug("change route done {}", s);
            return s1;
        }
        return "";
    }

    @Override
    public String delRoute(NetRoute route) {
        route.validate();
        String profileName = getCompatibilityProfileName(route.getInterfaceName());
        NetworkInterfaceInfo networkInterfaceInfo = getNetworkInterfaceInfo(route.getInterfaceName());
        if (networkInterfaceInfo != null) {
            // 删除永久路由
            if (route.getType().equals(NetRouteType.PERMANENT_ROUTE)) {
                List<String> cmds = new ArrayList<>();
                cmds.add("nmcli");
                cmds.add("con");
                cmds.add("mod");
                cmds.add(profileName);
                cmds.add("-ipv4.routes");
                if (route.getMetric() != null) {
                    cmds.add(String.format("%s %s %s", route.getTargetAndMask(), route.getGateway(), route.getMetric()));
                } else {
                    cmds.add(String.format("%s %s", route.getTargetAndMask(), route.getGateway()));
                }
                String s = execCmdAndReturn(cmds);
                String s1 = applyConf(profileName);
                log.debug("del route done {}", s);
                return s1;
            } else {
                List<String> cmds = new ArrayList<>();
                cmds.add("ip");
                cmds.add("route");
                cmds.add("del");
                cmds.add(route.getTargetAndMask());
                if (null != route.getGateway()) {
                    cmds.add("via");
                    cmds.add(route.getGateway());
                }
                cmds.add("dev");
                cmds.add(route.getInterfaceName());
                return execCmdAndReturn(cmds);
            }
        }
        return "";
    }


    @Override
    public WifiScanInfo scanWifiInfo(String interfaceName) {
        Process process = execCmd("nmcli -m tabular -f all dev wifi list --rescan yes ifname " + interfaceName);
        InputStream inputStream = process.getInputStream();

//        ClassPathResource classPathResource = new ClassPathResource("WifiScanInfo.log", this.getClass().getClassLoader());
//        InputStream inputStream = classPathResource.getStream();

        WifiScanInfo wifiScanInfo = new WifiScanInfo();
        wifiScanInfo.setInterfaceName(interfaceName);
        if (null != inputStream) {
            Map<String, Map<String, Object>> ssidMap = WIfiScanHelper.parseWifiScanInfo(inputStream);
            List<SSIDInfo> ssidList = wifiScanInfo.getSsidList();
            Map<String, SSIDInfo> ssidsMap = new HashMap<>();
            // group by ssid
            for (Map<String, Object> value : ssidMap.values()) {
                BSSIDInfo bssidInfo = new BSSIDInfo();
                bssidInfo.setBssid(MapUtil.getStr(value, "BSSID", null));
                bssidInfo.setMode(MapUtil.getStr(value, "MODE", null));
                bssidInfo.setSsid(MapUtil.getStr(value, "SSID", null));
                bssidInfo.setChannel(MapUtil.getStr(value, "CHAN", null));
                bssidInfo.setRate(MapUtil.getStr(value, "RATE", null));
                bssidInfo.setSignal(MapUtil.getStr(value, "SIGNAL", null));
                String freq = MapUtil.getStr(value, "FREQ", null);
                String state = MapUtil.getStr(value, "ACTIVE", null);
                if (connectionConfYesValue.contains(state)) {
                    bssidInfo.setState(ConState.connected);
                } else {
                    bssidInfo.setState(ConState.disconnect);
                }
                bssidInfo.setWaveBand(formatFreq(freq));
                SSIDInfo ssidInfo = ssidsMap.computeIfAbsent(bssidInfo.getSsid(), key -> new SSIDInfo());
                if (Objects.isNull(ssidInfo.getSsid())) {
                    ssidInfo.setSsid(bssidInfo.getSsid());
                    ssidInfo.setInterfaceName(interfaceName);
                    ssidInfo.setEncryption(MapUtil.getStr(value, "WPA-FLAGS", null));
                    ssidInfo.setNetworkType("--");
                    String security = MapUtil.getStr(value, "SECURITY", null);
                    ssidInfo.setSecureType(formatSecurityType(security));
                }
                ssidInfo.addBssid(bssidInfo);
            }
            ssidList.addAll(ssidsMap.values());
        }
        return wifiScanInfo;
    }

    private String formatFreq(String freq) {
        String[] split = freq.split("\\s+");
        if (split.length > 1) {
            if ("GHz".equals(split[1])) {
                return freq;
            } else if ("MHz".equals(split[1])) {
                BigDecimal bigDecimal = new BigDecimal(split[0]);
                String hz = bigDecimal.divide(new BigDecimal(1000)).setScale(1, BigDecimal.ROUND_DOWN).toString();
                return hz + " GHz";
            }
        }
        return freq;
    }

    private String formatSecurityType(String security) {
        if (security.equals("--")) {
            return WifiSecureType.OPEN.name();
        } else {
            String[] split1 = security.split(" ");
            if (split1.length > 1) {
                return split1[split1.length - 1];
            } else {
                return split1[0];
            }
        }
    }


    @Override
    public WifiLinkInfo getWifiLinkInfo(String interfaceName) {
        WifiLinkInfo wifiLinkInfo = new WifiLinkInfo();
        wifiLinkInfo.setDescription(interfaceName);
        wifiLinkInfo.setInterfaceName(interfaceName);
        wifiLinkInfo.setConfigFile("Unknown");

        WifiScanInfo wifiScanInfo = scanWifiInfo(interfaceName);
        SSIDInfo ssidInfo = wifiScanInfo.getSsidList().stream().filter(ssid -> ssid.getConState().equals(ConState.connected)).findFirst().orElse(null);
        if (Objects.nonNull(ssidInfo)) {

            BSSIDInfo bssidInfo = ssidInfo.getBssid().stream().filter(i -> i.getState().equals(ConState.connected)).findFirst().orElse(null);
            if (Objects.nonNull(bssidInfo)) {
                wifiLinkInfo.setState(ssidInfo.getConState());
                wifiLinkInfo.setSsid(ssidInfo.getSsid());
                wifiLinkInfo.setChannel(bssidInfo.getChannel());
                wifiLinkInfo.setBssid(bssidInfo.getBssid());
                wifiLinkInfo.setMode(bssidInfo.getMode());
                wifiLinkInfo.setSignal(bssidInfo.getSignal());
                wifiLinkInfo.setEncryption(ssidInfo.getEncryption());
                wifiLinkInfo.setSecureType(ssidInfo.getSecureType());
                wifiLinkInfo.setWaveBand(bssidInfo.getWaveBand());
                NetworkInterfaceInfo networkInterfaceInfo = getNetworkInterfaceInfo(interfaceName);
                String mac = networkInterfaceInfo.getMac();
                wifiLinkInfo.setMac(mac);
                return wifiLinkInfo;
            }
        }
        return wifiLinkInfo;
    }

    @Override
    public List<WifiLinkInfo> getWifiLinkInfos() {
        return getNetworkInterfaceInfos()
                .stream()
                .filter(i -> i.getType().equals(InterfaceType.wifi))
                .map(i -> getWifiLinkInfo(i.getId()))
                .collect(Collectors.toList());
    }

    @Override
    public WifiLinkConf getWifiConf(String interfaceName, String profileName) {
        return getWifiProfile(interfaceName, getWifiProfileName(profileName, interfaceName));
    }

    private WifiLinkConf getWifiProfile(String interfaceName, String profileName) {
        Process process = CommandUtils.execClassPathShFile("nmcli-ext.sh", "-f getWifiProfile -n " + profileName, this.getClass().getClassLoader());
        InputStream inputStream = process.getInputStream();
        try {
//            ClassPathResource classPathResource = new ClassPathResource("wifiConf", this.getClass().getClassLoader());
//            InputStream inputStream = classPathResource.getStream();

            if (null != inputStream) {
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                String line;
                WifiLinkConf conf = new WifiLinkConf();
                List<WifiLinkConf> confs = new ArrayList<>();
                while ((line = bufferedReader.readLine()) != null) {
                    line = line.trim();
                    if (line.isEmpty()) {
                        continue;
                    }

                    String[] split = line.split(": ");
                    if (split.length > 1) {
                        String value = split[1].trim();
                        if (line.contains(".id:")) {
                            conf = new WifiLinkConf();
                            confs.add(conf);
                            conf.setName(value);
                            continue;
                        }
                        if (line.contains("wireless.ssid:")) {
                            conf.setSsid(value);
                            continue;
                        }
                        if (line.contains("interface-name:")) {
                            conf.setInterfaceName(value);
                            continue;
                        }
                        if (line.contains("security.psk:")) {
                            conf.setPassword(value);
                        }
                        if (line.contains("autoconnect:")) {
                            if (connectionConfYesValue.contains(value)) {
                                conf.setAutoConnection(true);
                            } else {
                                conf.setAutoConnection(false);
                            }
                        }

                        if (line.contains("connection.uuid:")) {
                            conf.setId(value);
                            continue;
                        }
                        if (line.contains("security.key-mgmt:")) {
                            if (value.equals("wpa-psk")) {
                                // TODO
                                conf.setSecureType(WifiSecureType.WPA2.name());
                            } else if (value.equals("none")) {
                                conf.setSecureType(WifiSecureType.OPEN.name());
                            }
                        }
                    }
                }
                return confs.stream().filter(i -> i.getInterfaceName().equals(interfaceName)).findFirst().orElse(null);
            }
            return null;
        } catch (Exception e) {
            log.error("get conf error", e);
            return null;
        }
    }


    @Override
    public String connectWifi(WifiLinkConf wifiLinkConf) {
        try {
            String profileName = wifiLinkConf.getName();
            if (profileName == null || profileName.isEmpty()) {
                profileName = wifiLinkConf.getSsid();
                wifiLinkConf.setName(profileName);
            }

            if (wifiLinkConf.getSecureType() == null && wifiLinkConf.getPassword() == null) {
                wifiLinkConf.setSecureType(WifiSecureType.OPEN.name());
            }

            // 创建配置文件
            setWifiConf(wifiLinkConf);
            List<String> cmds = new ArrayList<>();
            cmds.add("nmcli");
            cmds.add("con");
            cmds.add("up");
            cmds.add(getWifiProfileName(wifiLinkConf.getName(), wifiLinkConf.getInterfaceName()));
            cmds.add("ifname");
            cmds.add(wifiLinkConf.getInterfaceName());
            String res = execCmdAndReturn(cmds);
            log.debug("连接wifi成功：{}", res);
            return res;
        } catch (Exception e) {
            log.error("连接wifi异常：{}", e.getMessage(), e);
            throw new CmdExecuteException("连接wifi异常：" + e.getMessage(), e);
        }
    }

    @Override
    public String disconnectWifi(String interfaceName) {
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("d");
        cmds.add("disconnect");
        cmds.add(interfaceName);
        return execCmdAndReturn(cmds);
    }

    @Override
    public String forgetWifi(String interfaceName, String profileName) {
        String s1 = disconnectWifi(interfaceName);
        String s = delWifiConf(interfaceName, profileName);
        log.debug("忘记wifi成功： {}", s);
        return String.format("disconnect: %s \n del profile: %s", s1, s);
    }

    @Override
    public String setWifiConf(WifiLinkConf wifiLinkConf) {
        String wifiProfileName = getWifiProfileName(wifiLinkConf.getName(), wifiLinkConf.getInterfaceName());
        WifiLinkConf wifiProfile = getWifiProfile(wifiLinkConf.getInterfaceName(), wifiProfileName);
        if (Objects.isNull(wifiProfile)) {
            List<String> cmds = new ArrayList<>();
            cmds.add("nmcli");
            cmds.add("con");
            cmds.add("add");
            cmds.add("con-name");
            cmds.add(wifiProfileName);
            cmds.add("ifname");
            cmds.add(wifiLinkConf.getInterfaceName());
            cmds.add("autoconnect");
            if (wifiLinkConf.isAutoConnection()) {
                cmds.add("yes");
            } else {
                cmds.add("no");
            }
            cmds.add("type");
            cmds.add("wifi");
            cmds.add("ssid");
            cmds.add(wifiLinkConf.getSsid());
            if (!wifiLinkConf.getSecureType().equals(WifiSecureType.OPEN.name())) {
                cmds.add("wifi-sec.key-mgmt");
                cmds.add("wpa-psk");
                cmds.add("wifi-sec.psk");
                cmds.add(wifiLinkConf.getPassword());
            }

            String s = execCmdAndReturn(cmds);
            log.debug("create wifi profile: {}", s);
            return s;
        } else {
            List<String> cmds = new ArrayList<>();
            cmds.add("nmcli");
            cmds.add("con");
            cmds.add("mod");
            cmds.add(wifiProfileName);
            // auto con change
            if (wifiLinkConf.isAutoConnection() != (wifiProfile.isAutoConnection())) {
                cmds.add("autoconnect");
                cmds.add(wifiLinkConf.isAutoConnection() ? "yes" : "no");
            }
            // update
            if (!WifiSecureType.OPEN.name().equals(wifiLinkConf.getSecureType())) {
                // password change
                if (!wifiLinkConf.getPassword().equals(wifiProfile.getPassword())) {
                    cmds.add("wifi-sec.psk");
                    cmds.add(wifiLinkConf.getPassword());
                }
            } else {
                // set wifi-sec.key-mgmt none
                cmds.add("key-mgmt");
                cmds.add("none");
            }
            if (cmds.size() > 4) {
                String res = execCmdAndReturn(cmds);
                log.debug("update wifi profile: {}", res);
                return res;
            }
        }
        log.debug("set wifi profile: no change");
        return "";
    }

    @Override
    public String delWifiConf(String interfaceName, String profileName) {
        String realProfileName = getWifiProfileName(profileName, interfaceName);
        WifiLinkConf wifiProfile = getWifiProfile(interfaceName, realProfileName);
        if (Objects.isNull(wifiProfile)) {
            log.warn("删除wifi配置文件： does not exist");
            return "conf " + realProfileName + "does not exist";
        }
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("con");
        cmds.add("delete");
        if (!Objects.isNull(wifiProfile.getId())) {
            cmds.add(wifiProfile.getId());
        } else {
            cmds.add(wifiProfile.getName());
        }
        String s = execCmdAndReturn(cmds);
        log.debug("删除wifi配置文件成功： {}", s);
        return s;
    }

    /**
     * 多wifi网卡会enabled所有的设备
     *
     * @param interfaceName 网卡名称
     * @return res
     */
    @Override
    public String enableWifi(String interfaceName) {
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("radio");
        cmds.add("wifi");
        cmds.add("on");
        String res = execCmdAndReturn(cmds);
        log.debug("启用wifi {}", res);
        return res;
    }

    /**
     * 多wifi网卡会断开所有的设备
     *
     * @param interfaceName 网卡名称
     * @return res
     */
    @Override
    public String disableWifi(String interfaceName) {
        List<String> cmds = new ArrayList<>();
        cmds.add("nmcli");
        cmds.add("radio");
        cmds.add("wifi");
        cmds.add("off");
        String res = execCmdAndReturn(cmds);
        log.debug("禁用用wifi {}", res);
        return res;
    }
}
